package crcache

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis"
	"github.com/olivere/elastic/v7"
	global2 "longmen/server/config/global"
	"longmen/server/internal/rpc/tencentim/imsdk"
	messages2 "longmen/server/internal/rpc/tencentim/messages"
	"longmen/server/internal/rpc/tencentim/messages/sports"
	"longmen/server/pkg/common/helper/const"
	"sync"
)

type (
	Score struct {
		MatchId     int `json:"match_id"`     // 比赛id
		MatchStatus int `json:"match_status"` // 比赛状态，详见状态码->比赛状态
		Striker     int `json:"striker"`      // 击球方，1-主队、2-客队、0-未知

		Round     int `json:"round"`      // 当前局
		AwayScore int `json:"away_score"` // 客队 局比分
		HomeScore int `json:"home_score"` // 主队 局比分

		AwayTotalScore int `json:"away_total_score"` // 客队总得分
		HomeTotalScore int `json:"home_total_score"` // 主队总得分
	}
	PublishScoreInterface interface {
		GetScore() Score
	}

	ScoreCache struct {
		rds redis.UniversalClient
		es  *elastic.Client
	}
)

var scoreInstance *ScoreCache
var scoreOnce sync.Once

func NewScoreCache() *ScoreCache {
	scoreOnce.Do(func() {
		scoreInstance = &ScoreCache{
			rds: global2.GetRds(),
			es:  global2.GetEs(),
		}
	})
	return scoreInstance
}

// 板球比分key
func (t *ScoreCache) genScoreKey(matchId int) string {
	return fmt.Sprintf("match_live:cricket_score:%d", matchId)
}

// PublishLiveScore 写入板球比分
func (t *ScoreCache) PublishLiveScore(records []PublishScoreInterface) error {
	data := make([]Score, 0)
	pp := t.rds.Pipeline()
	for _, record := range records {
		scores := record.GetScore()
		data = append(data, scores)
		msg := sports.CricketScoreMessage{
			SentMessageStruct: messages2.SentMessageStruct{Type: messages2.ScoreLiveMessageType},
			Scores:            scores,
		}
		marshal, _ := json.Marshal(msg)

		err := pp.Set(t.genScoreKey(scores.MatchId), string(marshal), CacheExpire).Err()
		if err != nil {
			global2.Log.Error("set cache error: ", err)
		}
		err = imsdk.GetSdk().BroadcastInterface(scores.MatchId, msg, 0)
		if err != nil {
			global2.Log.Errorf("Broadcast error: %v", err)
			return err
		}
	}
	_, err := pp.Exec()
	if err != nil {
		global2.Log.Errorf("Pipeline run error: %v", err)
	}
	t.writeToes(data)
	return nil
}

func (t *ScoreCache) LiveScore(records []Score) error {
	pp := t.rds.Pipeline()
	for _, record := range records {
		msg := sports.CricketScoreMessage{
			SentMessageStruct: messages2.SentMessageStruct{Type: messages2.ScoreLiveMessageType},
			Scores:            record,
		}
		marshal, _ := json.Marshal(msg)

		err := pp.Set(t.genScoreKey(record.MatchId), string(marshal), CacheExpire).Err()
		if err != nil {
			global2.Log.Error("set cache error: ", err)
		}
	}
	_, err := pp.Exec()
	if err != nil {
		global2.Log.Errorf("Pipeline run error: %v", err)
	}
	t.writeToes(records)
	return nil
}

// GetScoreCache 获取最新的板球比分
func (t *ScoreCache) GetScoreCache(matchId int) string {
	result, err := t.rds.Get(t.genScoreKey(matchId)).Result()
	if err != nil {
		return ""
	}
	return result
}

// 板球比分KEY
func (t *ScoreCache) getEsIndexName() string {
	return _const.ES_MATCH_CRICKET_SCORE
}

func (t *ScoreCache) InitEsIndex() {
	t.es.Index().Index(t.getEsIndexName())
}

func (t *ScoreCache) writeToes(sendData []Score) {
	bulks := make([]*elastic.BulkIndexRequest, 0)
	total := 3000 // 3000条批量写一次
	writeCount := 0
	for _, datum := range sendData {
		// 赛程ID
		id := fmt.Sprintf("%d", datum.MatchId)
		bulks = append(bulks, elastic.NewBulkIndexRequest().Index(t.getEsIndexName()).Doc(datum).Id(id))
		writeCount++
		if writeCount == total {
			t.bulkToEs(bulks)
			bulks = make([]*elastic.BulkIndexRequest, 0)
		}
	}

	if len(bulks) > 0 {
		t.bulkToEs(bulks)
	}

}

func (t *ScoreCache) bulkToEs(records []*elastic.BulkIndexRequest) {
	bulk := t.es.Bulk()
	for i := range records {
		bulk.Add(records[i])
	}
	res, err := bulk.Do(context.Background())
	if err != nil {
		global2.Log.Errorf("exec bulk to es : %v", err)
		return
	}
	for _, item := range res.Failed() {
		global2.Log.Errorf("write to es err: %s", item.Error.Reason)
	}
}
